// <!-- Copyright 2010 John L. Reilly All Rights Reserved.
package com.riq;

//import com.google.common.collect.Maps;
//import com.google.inject.Inject;

import java.io.Serializable;
import java.util.Collection;
import java.util.Map;

import javax.cache.Cache;
import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;

import com.google.appengine.repackaged.com.google.common.collect.Maps;
import com.riq.entity.HasPrimaryKey;


/**
 * An EntryStore that reads from cache first, then the datastore, and writes to
 * cache, then datastore.
 *
 * TODO(shakusa) For further speed-up add an in-memory version per request.
 * See java/com/google/corp/micasa/datastore/common/WebMemoryCache.java
 *
 * @author Steven Hakusa (shakusa@google.com)
 */
public class ReadWriteThroughDataStore implements DataStore { 

  private final PersistenceManagerFactory pmf;
  private final Cache cache;
  

  @Inject
  public ReadWriteThroughDataStore(PersistenceManagerFactory pmf, Cache cache) {

    this.pmf = pmf;
    this.cache = cache;
  }

  @Override
  public <T extends HasPrimaryKey> void makePersistent(T obj) {
    CacheKey cacheKey = new CacheKey(obj.getClass(), obj.getPrimaryKey());
    PersistenceManager pm = pmf.getPersistenceManager();
    try {
      pm.makePersistent(obj);
      cache.put(cacheKey, obj);
    } finally {
      pm.close();
    }
  }


  @Override
  public <T extends HasPrimaryKey> void makePersistAll(Collection<T> objs) {
    Map<CacheKey, T> map = Maps.newHashMap();
    for (T obj : objs) {
      map.put(new CacheKey(obj.getClass(), obj.getPrimaryKey()), obj);
    }

    PersistenceManager pm = pmf.getPersistenceManager();
    try {
      pm.makePersistentAll(objs);
      cache.putAll(map);
    } finally {
      pm.close();
    }
  }
    
  @Override
  public <T> T getByKey(Class<T> clazz, Object key) {
    CacheKey cacheKey = new CacheKey(clazz, key);
    @SuppressWarnings("unchecked")
    T obj = (T) cache.get(cacheKey);
    if (obj != null) {
      return obj;
    }
    PersistenceManager pm = pmf.getPersistenceManager();
    try {
      obj = pm.getObjectById(clazz, key);
    } catch (JDOObjectNotFoundException e) {
      return null;
    } finally {
      pm.close();
    }
    cache.put(cacheKey, obj);
    return obj;
  }
  
  @Override
  public <T> T deleteByKey(Object key) {
    @SuppressWarnings("unchecked")
    T obj = (T) cache.remove(key);
    PersistenceManager pm = pmf.getPersistenceManager();
    try {
      pm.deletePersistent(obj);
    } catch (JDOObjectNotFoundException e) {
      return null;
    } finally {
      pm.close();
    }
    return null;
  }

  public static class CacheKey implements Serializable {
	private static final long serialVersionUID = 1L;
    private final Class<?> clazz;
    private final Object key;

    public CacheKey(Class<?> clazz, Object key) {
      this.clazz = clazz;
      this.key = key;
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) {
        return true;
      }
      if (o == null || getClass() != o.getClass()) {
        return false;
      }

      CacheKey key1 = (CacheKey) o;

      if (clazz != null ? !clazz.equals(key1.clazz) : key1.clazz != null) {
        return false;
      }
      if (key != null ? !key.equals(key1.key) : key1.key != null) {
        return false;
      }

      return true;
    }

    @Override
    public int hashCode() {
      int result;
      result = (clazz != null ? clazz.hashCode() : 0);
      result = 31 * result + (key != null ? key.hashCode() : 0);
      return result;
    }
  }
  
  
  @Override
  public void clearCache() {
          cache.clear();
         }
	    
}

